﻿#region << 文 件 说 明 >>

/*----------------------------------------------------------------
// 文件名称：EditContainer
// 创 建 人：YangCheng
// 创建时间：2022/6/30 16:27:07
// 文件版本：V1.0.0
// ===============================================================
// 功能描述：
//
//
//----------------------------------------------------------------*/

#endregion

namespace Vampirewal.Core.WPF.Theme;

/// <summary>
///
/// </summary>
public class EditContainer : ListBox
{
    private class LocationInfo
    {
        public double SettingsItemLocation { get; set; }

        public double SettingsContainerLocation { get; set; }

        public RadioButton SettingsRb { get; set; }
    }

    #region Properties

    /// <summary>
    /// 属性项目标注，KeyValuePair<double,double>：key对应容器坐标，value选择项目坐标
    /// </summary>
    private Dictionary<string, LocationInfo> _dicSettingsItemContainerPoint;

    /// <summary>
    /// 设置选项定位名
    /// </summary>
    private string _locationName;

    private Border _cursor;
    private ItemsPresenter _itemP;
    private bool sbRunning;

    /// <summary>
    /// 内容项Y轴的偏移量
    /// </summary>
    private double _tmpYOffset;

    /// <summary>
    /// 最后一项高度偏移高度
    /// </summary>
    private double _lastItemHeightOffset;

    #region 动画元素

    private EasingDoubleKeyFrame _settingsLocationEasing;
    private Storyboard _settingsLocation;
    private Storyboard _mouseWheelMove;
    private EasingDoubleKeyFrame _mouseWheelMoveEasing;
    private Storyboard _location;
    private EasingDoubleKeyFrame _locationEasing;

    #endregion

    #endregion

    /// <summary>
    /// 
    /// </summary>
    public EditContainer()
    {
        //构造函数
        Loaded += EditContainer_Loaded;
    }

    private void EditContainer_Loaded(object sender, System.Windows.RoutedEventArgs e)
    {
        Loaded -= EditContainer_Loaded;
        _dicSettingsItemContainerPoint = new Dictionary<string, LocationInfo>();
        _mouseWheelMove = CreateSettingsItemStoryboard(0);
        _location = CreateSettingsItemStoryboard(0);
        _settingsLocation = CreateStoryboard(0);

        if (_mouseWheelMove.Children.First() is DoubleAnimationUsingKeyFrames key &&
            key.KeyFrames[0] is EasingDoubleKeyFrame eas &&
            _location.Children.First() is DoubleAnimationUsingKeyFrames lKey &&
            lKey.KeyFrames[0] is EasingDoubleKeyFrame lEas &&
            _settingsLocation.Children.First() is DoubleAnimationUsingKeyFrames sKey &&
            sKey.KeyFrames[0] is EasingDoubleKeyFrame sEas &&
            Template.FindName("itemsP", this) is ItemsPresenter ip &&
            Template.FindName("br_Cursor", this) is Border br &&
            Template.FindName("editItemsContainer", this) is StackPanel sp &&
            Template.Resources["RbTabStyle"] is Style rbStyle &&
            Template.FindName("sv", this) is Grid sv)
        {
            _itemP = ip;
            _itemP.SizeChanged += Can_SizeChanged;
            _cursor = br;
            eas.KeyTime = TimeSpan.Parse("0:0:0");
            _mouseWheelMoveEasing = eas;
            _locationEasing = lEas;
            _settingsLocationEasing = sEas;
            _location.Completed += _sb2_Completed;

            for (int i = 0; i < ItemContainerGenerator.Items.Count; i++)
            {
                var editItem = ItemContainerGenerator.ContainerFromIndex(i) as EditItem;
                editItem.Width = sv.ActualWidth;
                var rbTmp = CreateTitle(rbStyle, editItem);
                sp.Children.Add(rbTmp);
            }
            sv.AddHandler(Grid.PreviewMouseWheelEvent, new MouseWheelEventHandler(Sv_PreviewMouseWheel));
            sv.SizeChanged += Sv_SizeChanged;
            //if (!string.IsNullOrEmpty(_locationName))
            //    RunStoryboard(_locationName);
        }
    }

    private void Can_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        if (Template.FindName("editItemsContainer", this) is StackPanel sp &&
            Template.Resources["RbTabStyle"] is Style rbStyle &&
            Template.FindName("sv", this) is Grid sv)
        {
            sp.Children.Clear();
            _dicSettingsItemContainerPoint.Clear();
            for (int i = 0; i < ItemContainerGenerator.Items.Count; i++)
            {
                var editItem = ItemContainerGenerator.ContainerFromIndex(i) as EditItem;
                editItem.Width = sv.ActualWidth;
                var rbTmp = CreateTitle(rbStyle, editItem);
                sp.Children.Add(rbTmp);
                sp.UpdateLayout();
                _dicSettingsItemContainerPoint.Add(rbTmp.Tag.ToString(), new LocationInfo()
                {
                    SettingsContainerLocation = 0 - editItem.TranslatePoint(new Point(0, 0), sp).Y,
                    SettingsItemLocation = rbTmp.TranslatePoint(new Point(0, 0), sp).Y,
                    SettingsRb = rbTmp
                });
                _lastItemHeightOffset = editItem.ActualHeight - this.ActualHeight;
            }
        }
    }

    private void Sv_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        for (int i = 0; i < ItemContainerGenerator.Items.Count; i++)
        {
            var editItem = ItemContainerGenerator.ContainerFromIndex(i) as EditItem;
            editItem.Width = e.NewSize.Width;
        }
    }

    private void Sv_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
    {
        if (!sbRunning)
        {
            if (e.Delta > 0 && _tmpYOffset <= 0)
            {
                _tmpYOffset = (_tmpYOffset + 48) > 0 ? 0 : _tmpYOffset + 48;
            }
            else if (e.Delta < 0 && _tmpYOffset >= _dicSettingsItemContainerPoint.Last().Value.SettingsContainerLocation)
            {
                _tmpYOffset = (_tmpYOffset - 48) <= _dicSettingsItemContainerPoint.Last().Value.SettingsContainerLocation ? _dicSettingsItemContainerPoint.Last().Value.SettingsContainerLocation : _tmpYOffset - 48;
            }
            if (_tmpYOffset <= 0)
            {
                var top = _dicSettingsItemContainerPoint.Last(t => t.Value.SettingsContainerLocation >= _tmpYOffset - 96);
                if (_settingsLocationEasing.Value != top.Value.SettingsItemLocation)
                {
                    _settingsLocationEasing.Value = top.Value.SettingsItemLocation;
                    top.Value.SettingsRb.IsChecked = true;
                    _settingsLocation.Begin(_cursor);
                }
                _mouseWheelMoveEasing.Value = _tmpYOffset;
                _mouseWheelMove.Begin(_itemP);
            }
        }
        e.Handled = true;
    }

    private void RunStoryboard(string settingsItemName)
    {
        _settingsLocationEasing.Value = _dicSettingsItemContainerPoint[settingsItemName].SettingsItemLocation;
        _settingsLocation.Begin(_cursor);

        _tmpYOffset = _dicSettingsItemContainerPoint[settingsItemName].SettingsContainerLocation;
        _locationEasing.Value = _tmpYOffset;
        _dicSettingsItemContainerPoint[settingsItemName].SettingsRb.IsChecked = true;
        sbRunning = true;
        _location.Begin(_itemP);
    }

    private void _sb2_Completed(object sender, EventArgs e)
    {
        sbRunning = false;
    }

    #region Tools

    private RadioButton CreateTitle(Style rbStyle, EditItem item)
    {
        RadioButton rb = new RadioButton();
        rb.Tag = Guid.NewGuid().ToString();
        Binding bind = new Binding("Visibility");
        bind.Source = item;
        rb.SetBinding(RadioButton.VisibilityProperty, bind);
        rb.GroupName = "EditPageGroup";
        rb.Style = rbStyle;
        rb.Content = item.HeaderText;
        rb.Click += StackPanel_Checked;
        return rb;
    }

    private void StackPanel_Checked(object sender, RoutedEventArgs e)
    {
        if (e.OriginalSource is RadioButton btn)
        {
            RunStoryboard(btn.Tag.ToString());
        }
    }

    #region 动画创建

    private Storyboard CreateStoryboard(double y)
    {
        Storyboard sb = new Storyboard();
        DoubleAnimationUsingKeyFrames keyFrames = new DoubleAnimationUsingKeyFrames();
        EasingDoubleKeyFrame doubleKeyFrame = new EasingDoubleKeyFrame();
        doubleKeyFrame.KeyTime = TimeSpan.Parse("0:0:0.7");
        doubleKeyFrame.Value = y;
        BackEase effect = new BackEase();
        effect.EasingMode = EasingMode.EaseOut;
        effect.Amplitude = 0.4;
        doubleKeyFrame.EasingFunction = effect;
        keyFrames.KeyFrames.Add(doubleKeyFrame);
        Storyboard.SetTargetProperty(keyFrames, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)"));
        Storyboard.SetTarget(keyFrames, _cursor);
        sb.Children.Add(keyFrames);
        return sb;
    }

    private Storyboard CreateSettingsItemStoryboard(double y)
    {
        Storyboard sb = new Storyboard();
        DoubleAnimationUsingKeyFrames keyFrames = new DoubleAnimationUsingKeyFrames();
        EasingDoubleKeyFrame doubleKeyFrame = new EasingDoubleKeyFrame();
        doubleKeyFrame.KeyTime = TimeSpan.Parse("0:0:0.5");
        doubleKeyFrame.Value = y;
        BackEase effect = new BackEase();
        effect.EasingMode = EasingMode.EaseInOut;
        effect.Amplitude = 0.2;
        doubleKeyFrame.EasingFunction = effect;
        keyFrames.KeyFrames.Add(doubleKeyFrame);
        Storyboard.SetTargetProperty(keyFrames, new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.Y)"));
        Storyboard.SetTarget(keyFrames, _itemP);
        sb.Children.Add(keyFrames);
        return sb;
    }

    #endregion

    #endregion

    static EditContainer()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(EditContainer), new FrameworkPropertyMetadata(typeof(EditContainer)));
    }

    /// <inheritdoc/>
    protected override bool IsItemItsOwnContainerOverride(object item)
    {
        return item is EditItem;
    }

    /// <inheritdoc/>
    protected override DependencyObject GetContainerForItemOverride()
    {
        return new EditItem();
    }

    #region 依赖属性

    #region 左侧

    /// <summary>
    /// 左侧背景色
    /// </summary>
    public Brush LeftBackground
    {
        get { return (Brush)GetValue(LeftBackgroundProperty); }
        set { SetValue(LeftBackgroundProperty, value); }
    }

    // Using a DependencyProperty as the backing store for LeftBackground.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty LeftBackgroundProperty =
        DependencyProperty.Register("LeftBackground", typeof(Brush), typeof(EditContainer), new PropertyMetadata(Brushes.WhiteSmoke));

    /// <summary>
    /// 左侧前景色
    /// </summary>
    public Brush LeftForeground
    {
        get { return (Brush)GetValue(LeftForegroundProperty); }
        set { SetValue(LeftForegroundProperty, value); }
    }

    // Using a DependencyProperty as the backing store for LeftForeground.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty LeftForegroundProperty =
        DependencyProperty.Register("LeftForeground", typeof(Brush), typeof(EditContainer), new PropertyMetadata(Brushes.Black));

    public Brush MouseOverColor
    {
        get { return (Brush)GetValue(MouseOverColorProperty); }
        set { SetValue(MouseOverColorProperty, value); }
    }

    // Using a DependencyProperty as the backing store for MouseOverColor.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty MouseOverColorProperty =
        DependencyProperty.Register("MouseOverColor", typeof(Brush), typeof(EditContainer), new PropertyMetadata(Brushes.Chocolate));

    public Brush CheckedColor
    {
        get { return (Brush)GetValue(CheckedColorProperty); }
        set { SetValue(CheckedColorProperty, value); }
    }

    // Using a DependencyProperty as the backing store for CheckedColor.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty CheckedColorProperty =
        DependencyProperty.Register("CheckedColor", typeof(Brush), typeof(EditContainer), new PropertyMetadata(Brushes.Chocolate));

    #endregion

    #region 中间

    public Brush ContainerBackground
    {
        get { return (Brush)GetValue(ContainerBackgroundProperty); }
        set { SetValue(ContainerBackgroundProperty, value); }
    }

    // Using a DependencyProperty as the backing store for ContainerBackground.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ContainerBackgroundProperty =
        DependencyProperty.Register("ContainerBackground", typeof(Brush), typeof(EditContainer), new PropertyMetadata(Brushes.White));

    #endregion

    #endregion
}